<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script type="text/javascript">
    // 看一个具体的示例，即同一个属性名同时出现在对象的自身属性和原型属性中
    function Gadget(name) {
        this.name = name;
    }
    console.log(Gadget.prototype.name = 'mirror');

    // 然后我们新建一个对象，并访问该对象自身的name属性
    var toy = new Gadget('camera');
    console.log(toy.name);

    // 我们可以通过hasOwnProperty()方法来判断一个属性是自身属性还是原型属性
    console.log(toy.hasOwnProperty('name'));

    // 这时候，如果我们删除这个属性，同名的原型属性就会'浮出水面'
    console.log(delete toy.name);
    console.log(toy.name);
    console.log(toy.hasOwnProperty('name'));

    // 当然，我们随时都可以重建这个对象的自身属性
    toy.name = 'camera';
    console.log(toy.name);

    // 如何判断一个对象的某个原型属性到底是原型链中的哪一个原型的属性呢？答案仍然是使用hasOwnProperty()属性。例如，我们想知道toString属性来自于哪里
    console.log(toy.toString());
    console.log(toy.hasOwnProperty('toString'));
    console.log(toy.constructor.hasOwnProperty('toString'));
    console.log(toy.constructor.prototype.hasOwnProperty('toString'));
    console.log(Object.hasOwnProperty('toString'));
    console.log(Object.prototype.hasOwnProperty('toString'));

    // 枚举属性
    // 如果想获得每个对象的所有属性列表，我们可以使用for-in循环。在第二章：基本数据类型、数组、循环及条件表达式中，我们已经知道了如何使用该循环来遍历数组中的所有元素。当时我们提到，for更适合数组而for-in更适合对象。让我们以构造URL字符串为例
    var params = {
        productid: 666,
        section: 'products'
    };

    var url = 'http://example.org/page.php?',
        i,
        query = [];

    for(i in params){
        query.push(i + '=' + params[i]);
    }
    url += query.join('&');
    console.log(url);

    // 在这里，有些细节需要留意
    // 1、并不是所有的属性都会在for-in循环中显示。例如（数组的）length属性和constructor属性就不会被显示。那些会显示的属性被称为可枚举的，我们可以通过各个对象所提供的的propertyIsEnumerable()方法来判断对象的某个属性是否可枚举。在ES5中，我们可以具体指定那些属性可以枚举，而在ES3中没有这个功能
    // 2、原型链中的各个原型属性也会被显示出来，当然前提是他们是可以被枚举的。我们可以通过对象的hasOwnProperty()方法来判断一个属性是自身属性还是原型属性
    // 3、对于所有的原型属性，properytIsEnumerable()都会返回false，包括那些在for-in循环中可枚举的属性

    // 下面来看看这些方法具体是如何使用的。首先，我们来定义一个简化版的Gadget():
    function Gadget(name, color){
        this.name = name;
        this.color = color;
        this.getName = function() {
            return this.name;
        };
    }
    Gadget.prototype.price = 100;
    Gadget.prototype.rating = 3;

    // 然后新建一个对象
    var newtoy = new Gadget('webcam', 'black');

    // 现在如果对他执行for-in循环，就会列出该对象中的所有属性，包括原型中的属性
    // 其结果甚至包括该对象的方法（因为方法本质上也可以被视为是函数类型的属性）
    for(var prop in newtoy) {
        console.log(prop + '=' + newtoy[prop]);
    }

    // 如果要对对象属性和原型属性做一个区分，就需要调用hasOwnProperty()方法，我们可以先来试一下
    console.log(newtoy.hasOwnProperty('name'));
    console.log(newtoy.hasOwnProperty('price'));

    // 下面我们再来循环一次，不过这次只显示对象的自身属性
    for(var prop in newtoy) {
        if(newtoy.hasOwnProperty(prop)){
            console.log(prop + '=' + newtoy[prop]);
        }
    }

    // 再试试propertyIsEnumerable()，该方法会对所有的非内键对象属性返回true
    console.log(newtoy.propertyIsEnumerable('name'));

    // 对于内建属性和方法来说，他们大部分是不可枚举的
    console.log(newtoy.propertyIsEnumerable('constructor'));

    // 但是需要注意的是，如果propertyIsEnumerable()的调用时来自原型链上的某个对象，那么该对象中的属性是可枚举的
    console.log(newtoy.constructor.prototype.propertyIsEnumerable('price'));
</script>
</body>
</html>
